﻿using System;
using System.Configuration;
using System.IO;
using System.Web;
using System.Web.Configuration;

namespace Mistral.Configuration
{
	/// <summary>
	/// Менеджер стандартной конфигурации .Net
	/// Отличие в том, что можно задать альтернативный конфиг из которого и будут браться данные
	/// </summary>
	public class AppConfigManager
	{
		#region Private fields

		/// <summary>
		/// Путь к альтернативному конфигу. Если не задан используется обычный конфиг.
		/// </summary>
		private string _profileConfigFileName;

		#endregion

		public static readonly AppConfigManager Instance = new AppConfigManager();

		/// <summary>
		/// Private ctor
		/// </summary>
		private AppConfigManager( )
		{}

		#region Расширения для ConfigurationManager 

		/// <summary>
		/// Установка альтернативного конфига
		/// </summary>
		/// <param name="profileConfigFileName">Путь к конфигу</param>
		public void SetProfileConfig( string profileConfigFileName )
		{
			_profileConfigFileName = profileConfigFileName;
			GetProfileConfig();
		}

		/// <summary>
		/// Получение .Net конфига определенного для профиля. <see cref="SetProfileConfig"/>
		/// </summary>
		public System.Configuration.Configuration GetProfileConfig()
		{
			System.Configuration.Configuration configuration = null;
			
			if( _profileConfigFileName != null && File.Exists( _profileConfigFileName ) )
			{
				ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
				fileMap.ExeConfigFilename = _profileConfigFileName;
				configuration = ConfigurationManager.OpenMappedExeConfiguration( fileMap, ConfigurationUserLevel.None );
			}
	
			return configuration;
		}

		/// <summary>
		/// Получение секции по имени.
		/// </summary>
		/// <typeparam name="TSectionType">Тип секции</typeparam>
		/// <param name="sectionName">Имя секции</param>
		/// <returns>Секция</returns>
		public TSectionType GetSection<TSectionType>( string sectionName )
			where TSectionType: ConfigurationSection
		{
			return GetSection<TSectionType>( sectionName, true );
		}

		/// <summary>
		/// Получение секции по имени.
		/// </summary>
		/// <typeparam name="TSectionType">Тип секции</typeparam>
		/// <param name="sectionName">Имя секции</param>
		/// <param name="throwException">Выкидывать исключение в случае ошибок</param>
		/// <returns>Секция</returns>
		public TSectionType GetSection<TSectionType>( string sectionName, bool throwException )
			where TSectionType: ConfigurationSection
		{
			TSectionType section;
			string configFileName = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

			// Получение конфига
			System.Configuration.Configuration profileConfig = GetProfileConfig();
			if( profileConfig != null )
			{
				configFileName = profileConfig.FilePath;
				if( !profileConfig.HasFile && throwException )
				{
					string message = "Файл конфигурации '{0}' не найден.".SetArgs( configFileName );
					throw new System.Configuration.ConfigurationException( message );
				}
				section = (TSectionType)profileConfig.GetSection( sectionName );
			}
			else
			{
				// Получение секции из app.config или web.config
				section = GetSectionFromAppConfig<TSectionType>( sectionName );
			}

			if( section == null && throwException )
			{
				string message = "Секция '{0}' не найдена в конфигурационном файле '{1}'.".SetArgs( sectionName, configFileName );
				throw new ConfigurationErrorsException( message );
			}

			return section;
		}

		private TSectionType GetSectionFromAppConfig<TSectionType>( string sectionName )
		{
			TSectionType section;
			bool isWebApplication = HttpContext.Current != null;
			if( isWebApplication )
			{
				section = (TSectionType)WebConfigurationManager.GetSection( sectionName );
			}
			else
			{
				section = (TSectionType)ConfigurationManager.GetSection( sectionName );
			}
			return section;
		}

		#endregion
	}
}
